//
// RS Game Framework
// Copyright © 2009 Jedd Haberstro
// jhaberstro@gmail.com
// 
// $Id:
//

#ifndef RS_PORTABILITY_ARCHITECTURE_X64_ATOMIC_IPP
#define RS_PORTABILITY_ARCHITECTURE_X64_ATOMIC_IPP

#ifndef RS_ATOMIC_IMPLEMENTATION_GUARD
#   error "Do not include this file directly! Instead include rs/Atomic.hpp"
#endif

#if RS_COMPILER != RS_COMPILER_GCC && RS_COMPILER != RS_COMPILER_CLANG
#	error "Unsupported compiler. Assembly was written for GCC assembler."
#endif

namespace rs
{
	namespace atomic
	{
		inline void MemoryBarrier() {
			asm volatile ( "mfence" ::: "memory");
		}

		inline void ReadBarrier() {
			asm volatile ("lfence" ::: "memory");
		}

		inline void WriteBarrier() {
			asm volatile ("sfence" ::: "memory");
		}

		inline void CompilerBarrier() {
			asm volatile ("" ::: "memory");
		}


		// 8-bit
		inline Int8 AddFetch(Int8 volatile* address, Int8 value) {
			UInt8 result = AddFetch(reinterpret_cast< UInt8 volatile* >(address), reinterpret_cast< UInt8& >(value));
			return reinterpret_cast< Int8& >(result);
		}

		inline UInt8 AddFetch(UInt8 volatile* address, UInt8 value) {
			UInt8 oldValue = value;
			asm volatile (
				"lock; xaddb %0, %1\n\t"
				: "+r" (value), "+m" (*address)
				:: "memory"
			);

			return (value + oldValue);
		}

		inline Boolean CompareSwap(Int8 volatile* address, Int8 oldValue, Int8 newValue) {
			return CompareSwap(reinterpret_cast< UInt8 volatile* >(address), reinterpret_cast< UInt8& >(oldValue), reinterpret_cast< UInt8& >(newValue));
		}

		inline Boolean CompareSwap(UInt8 volatile* address, UInt8 oldValue, UInt8 newValue) {
			UInt8 expected = oldValue;
			asm volatile (
				"lock; cmpxchgb %[newValue], %[address]\n\t"
				: "+a" (oldValue), [address] "+m" (*address)
				: [newValue] "r" (newValue)
				: "memory"
			);

			return (expected == oldValue);
		}

		inline Int8 Exchange(Int8 volatile* address, Int8 value) {
			UInt8 result = Exchange(reinterpret_cast< UInt8 volatile* >(address), reinterpret_cast< UInt8& >(value));
			return reinterpret_cast< Int8& >(result);
		}

		inline UInt8 Exchange(UInt8 volatile* address, UInt8 value) {
			asm volatile(
				"lock; xchgb %0, %1\n\t"
				: "+r" (value), "+m" (*address)
				:: "memory"
			);

			return value;
		}


		// 16-bit
		inline Int16 AddFetch(Int16 volatile* address, Int16 value) {
			UInt16 result = AddFetch(reinterpret_cast< UInt16 volatile* >(address), reinterpret_cast< UInt16& >(value));
			return reinterpret_cast< Int16& >(result);
		}

		inline UInt16 AddFetch(UInt16 volatile* address, UInt16 value) {
			UInt16 oldValue = value;
			asm volatile (
				"lock; xaddw %0, %1\n\t"
				: "+r" (value), "+m" (*address)
				:: "memory"
			);

			return (value + oldValue);
		}

		inline Boolean CompareSwap(Int16 volatile* address, Int16 oldValue, Int16 newValue) {
			return CompareSwap(reinterpret_cast< UInt16 volatile* >(address), reinterpret_cast< UInt16& >(oldValue), reinterpret_cast< UInt16& >(newValue));
		}

		inline Boolean CompareSwap(UInt16 volatile* address, UInt16 oldValue, UInt16 newValue) {
			UInt16 expected = oldValue;
			asm volatile (
				"lock; cmpxchgw %[newValue], %[address]\n\t"
				: "+a" (oldValue), [address] "+m" (*address)
				: [newValue] "r" (newValue)
				: "memory"
			);

			return (expected == oldValue);
		}

		inline Int16 Exchange(Int16 volatile* address, Int16 value) {
			UInt16 result = Exchange(reinterpret_cast< UInt16 volatile* >(address), reinterpret_cast< UInt16& >(value));
			return reinterpret_cast< Int16& >(result);
		}

		inline UInt16 Exchange(UInt16 volatile* address, UInt16 value) {
			asm volatile(
				"lock; xchgw %0, %1\n\t"
				: "+r" (value), "+m" (*address)
				:: "memory"
			);

			return value;
		}


		// 32-bit
		inline Int32 AddFetch(Int32 volatile* address, Int32 value) {
			UInt32 result = AddFetch(reinterpret_cast< UInt32 volatile* >(address), reinterpret_cast< UInt32& >(value));
			return reinterpret_cast< Int32& >(result);
		}

		inline UInt32 AddFetch(UInt32 volatile* address, UInt32 value) {
			UInt32 oldValue = value;
			asm volatile (
				"lock; xaddl %0, %1\n\t"
				: "+r" (value), "+m" (*address)
				:: "memory"
			);

			return (value + oldValue);
		}

		inline Boolean CompareSwap(Int32 volatile* address, Int32 oldValue, Int32 newValue) {
			return CompareSwap(reinterpret_cast< UInt32 volatile* >(address), reinterpret_cast< UInt32& >(oldValue), reinterpret_cast< UInt32& >(newValue));
		}

		inline Boolean CompareSwap(UInt32 volatile* address, UInt32 oldValue, UInt32 newValue) {
			UInt32 expected = oldValue;
			asm volatile (
				"lock; cmpxchgl %[newValue], %[address]\n\t"
				: "+a" (oldValue), [address] "+m" (*address)
				: [newValue] "r" (newValue)
				: "memory"
			);

			return (expected == oldValue);
		}

		inline Int32 Exchange(Int32 volatile* address, Int32 value) {
			UInt32 result = Exchange(reinterpret_cast< UInt32 volatile* >(address), reinterpret_cast< UInt32& >(value));
			return reinterpret_cast< Int32& >(result);
		}

		inline UInt32 Exchange(UInt32 volatile* address, UInt32 value) {
			asm volatile(
				"lock; xchgl %0, %1\n\t"
				: "+r" (value), "+m" (*address)
				:: "memory"
			);

			return value;
		}


		// 64-bit
		inline Int64 AddFetch(Int64 volatile* address, Int64 value) {
			UInt64 result = AddFetch(reinterpret_cast< UInt64 volatile* >(address), reinterpret_cast< UInt64& >(value));
			return reinterpret_cast< Int64& >(result);
		}

		inline UInt64 AddFetch(UInt64 volatile* address, UInt64 value) {
			UInt64 oldValue = value;
			asm volatile (
				"lock; xaddq %0, %1\n\t"
				: "+r" (value), "+m" (*address)
				:: "memory"
			);

			return (value + oldValue);
		}

		inline Boolean CompareSwap(Int64 volatile* address, Int64 oldValue, Int64 newValue) {
			return CompareSwap(reinterpret_cast< UInt64 volatile* >(address), reinterpret_cast< UInt64& >(oldValue), reinterpret_cast< UInt64& >(newValue));
		}

		inline Boolean CompareSwap(UInt64 volatile* address, UInt64 oldValue, UInt64 newValue) {
			UInt64 expected = oldValue;
			asm volatile (
				"lock; cmpxchgq %[newValue], %[address]\n\t"
				: "+a" (oldValue), [address] "+m" (*address)
				: [newValue] "r" (newValue)
				: "memory"
			);

			return (expected == oldValue);
		}

		inline Int64 Exchange(Int64 volatile* address, Int64 value) {
			UInt64 result = Exchange(reinterpret_cast< UInt64 volatile* >(address), reinterpret_cast< UInt64& >(value));
			return reinterpret_cast< Int64& >(result);
		}

		inline UInt64 Exchange(UInt64 volatile* address, UInt64 value) {
			asm volatile(
				"lock; xchgq %0, %1\n\t"
				: "+r" (value), "+m" (*address)
				:: "memory"
			);

			return value;
		}
	}
}

#endif // RS_PORTABILITY_ARCHITECTURE_X64_ATOMIC_IPP